/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  					        magic_university.cpp

	$Header: /game/magic_university.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "magic_university.h"

#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "ai_value.h"
#include "army.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_window.h"
#include "creature_stack.h"
#include "enum_operations.h"
#include "external_string.h"
#include "hero.h"
#include "hero_keyword_replacer.h"
#include "mouse_event.h"
#include "music.h"
#include "object_registration.h"
#include "player.h"
#include "random.h"
#include "replace_keywords.h"
#include "select_skill_from_set.h"
#include "seminary_window.h"
#include "simple_dialog.h"
#include "skill.h"
#include "skill_properties.h"
#include "skill_set.h"

extern t_external_string const k_text_dead_heroes_ineligible;

namespace
{
	int const k_current_map_format_version = 1;

	/*
	** Both Magic University and War College/University are in this file, since
	** their functionality is similar.
	*/
	t_object_registration<t_magic_university> k_registration( k_adv_object_school, 
															  k_school_magic_university );

	t_object_registration<t_war_college> k_war_university_registration( k_adv_object_school, 
																		k_school_war_college );

	int const k_skill_upgrade_cost = 2000;
}
/*
** Selected hero can buy secondary magic skills.
*/
t_magic_university::t_magic_university( t_stationary_adventure_object const& source )
         : t_stationary_adventure_object( source ),
		   m_available_skills( ~t_skill_set() )
{
}

void t_magic_university::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                                    t_direction direction, t_adventure_frame* frame )
{
	int                  i;
	t_hero*              hero;
	std::vector<t_hero*> heroes;
	t_creature_array&    creatures = army->get_creatures();
	std::string          text;
    int                  random_skill_index = 0;

 	if (army->get_owner_number() >= 0)
		m_visited[army->get_owner_number()] = true;
	else
		return; // A gray army shouldn't be here.

	// AI decider
	if (army->get_owner()->is_computer()) 
	{
		for (int i = 0; i < t_creature_array::k_size; i++) 
		{
			t_hero* hero  = (*army)[i].get_hero();
			if (!hero) continue;
			
			t_skill_type type = get_best_compatible_skill( hero, army );
			while (type != k_skill_none) 
			{
				hero->learn_skill(t_skill(type, k_mastery_basic), false );
				army->get_owner()->spend( k_gold, k_skill_upgrade_cost );
				type = get_best_compatible_skill( hero, army );
			}
		}
		return;
	}

    /*
    ** Check for eligible heroes.
    */
	bool                    dead_heroes = true;
	bool                    any_heroes = false;
	for (i = 0; i < t_creature_array::k_size; i++)
	{
		hero = creatures[i].get_hero();

        // Hero or not?        
		if (hero == 0)
			continue;

        any_heroes = true;

		if (hero->is_dead())
			continue;
	    dead_heroes = false;

		heroes.push_back( hero );
	}

	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_university )->play( get_sound_volume() );
	std::string status_text;
    if (any_heroes && (dead_heroes == true))
    {
        status_text = get_text("heroes_dead");
        if (status_text == "")
            status_text = k_text_dead_heroes_ineligible;
  		ok_dialog( status_text, get_dialog_bottom( point ) );
		play_music( music );
        return;
    }
    // No heroes, show app. message...
    else if (!any_heroes)
    {
        status_text = get_text ("help.empty");
	    ok_dialog( status_text, get_dialog_bottom( point ) );
		play_music( music );
        return;
    }

	select_heroes_dialog ( heroes, frame, army);
	play_music( music );
}

/*--------------------------------------------------------------------*\
** Default available skills for old map files
\*--------------------------------------------------------------------*/
t_skill_set const & t_magic_university::get_default_available_skill_set()
{
	return ::get_skill_set(false, true, true, false);
}

/*--------------------------------------------------------------------*\
** Set the 4 available skills for purchase.
\*--------------------------------------------------------------------*/
int const k_university_skill_count = 4;

void t_magic_university::set_available_skills()
{
	t_adventure_map * map = get_map();
	assert( map );

	t_skill_set used_skills;
	t_skill_set available_skills = m_available_skills & map->get_allowed_skills();
	t_skill_set required_skills = m_required_skills;

	for (int i = 0; i < k_university_skill_count; i++)
	{
		if ( available_skills.none() )
		{
			// Fall back positions... Map editor does not enforce having 4 allowed skills,
			// let alone 4 skills common to the university's available skills and the map's
			// allowed skills
			available_skills = m_available_skills & ~used_skills;

			if ( available_skills.none() )
			{
				available_skills = map->get_allowed_skills() & ~used_skills;
				if ( available_skills.none() )
				{
					available_skills = get_default_available_skill_set() & ~used_skills;
				}
			}
		}

		t_skill_type skill = select_skill_from_set(available_skills, required_skills);

		available_skills[skill] = false;
		required_skills[skill] = false;
		used_skills[skill] = true;

		m_skills.push_back(skill);
	}
}

// -----------------------------------------------------------------------------------------
// Skill & hero select dialog - put here so we can change text names if necessary.
// -----------------------------------------------------------------------------------------
void t_magic_university::select_heroes_dialog( std::vector<t_hero*> const& heroes, t_window* parent, t_army* army )
{
    t_seminary_window* seminary_ptr = new t_seminary_window( army, 0, 
									*this, get_name(), m_skills, parent ); 

	seminary_ptr->run_modal();
}

// ------------------------------------------------------------------------
// Read in primary magic skills for save game.
// ------------------------------------------------------------------------
bool t_magic_university::read( std::streambuf&						stream, 
						       t_qualified_adv_object_type const&	type,
						       int									version )
{
    int i;

	if (version < 1)
	{
		// Choose randomly
		m_available_skills = get_default_available_skill_set();
		m_required_skills.reset();

		return true;
	}

	if (version < 2)
	{
		int old_single_use_id = get<int>( stream );
	}

	for (i=0; i<k_university_skill_count; i++)
	{
		m_skills.push_back ( t_skill_type( get<t_uint8>( stream )) );
	}

	return m_visited.read( stream );
}

// ------------------------------------------------------------------------
// Read from new map
// ------------------------------------------------------------------------
bool t_magic_university::read_from_map( std::streambuf& stream, t_progress_handler* progress)
{
	int version = get< t_uint16 >( stream );
	if ( version < 0 || version > k_current_map_format_version )
		return false;

	if (version == 0)
	{
		m_available_skills = get_default_available_skill_set();
		m_required_skills.reset();
	}
	else
	{
		if (!m_available_skills.read( stream ) ||
			!m_required_skills.read( stream ))
		{
			return false;
		}
	}
	
	return true;
}

void t_magic_university::right_click( t_mouse_event const& event,
	                                  t_adventure_frame* adventure_frame )
{
	t_skill_mastery information = get_information_level();
	int				player_number = get_map()->get_player_number();

	if (information >= k_mastery_basic)
		m_visited[player_number] = true;
	if (!m_visited[player_number])
	{
		t_stationary_adventure_object::right_click( event, adventure_frame );
		return;
	}
	std::string				help_text = get_text( "help_skills" );
	t_hero_keyword_replacer replacer;
	int						i;

	for (i = 0; i < m_skills.size(); ++i)
		replacer.add_skill( t_skill( m_skills[i], k_mastery_basic ) );

	show_popup_text( replacer( help_text ), event.screen_point );
}

// ------------------------------------------------------------------------
// Place new magic university
// ------------------------------------------------------------------------
void t_magic_university::place( t_adventure_map &		map,
						        t_adv_map_point const&	point )
{
	t_stationary_adventure_object::place( map, point );

	if ( m_skills.empty() )
	{
		// Choose skills. We can't do this in read_from_map() since we don't have
		// the map's list of allowed skills yet
		set_available_skills();
	}
}

// ------------------------------------------------------------------------
// Write out primary magic skills for save game.
// ------------------------------------------------------------------------
bool t_magic_university::write( std::streambuf& stream ) const
{
    int i;

    for (i = 0; i < m_skills.size(); i++)
    {
    	put<t_uint8>( stream, m_skills[i] );
    }

	return m_visited.write( stream );
}

/*--------------------------------------------------------------------*\
** Return version.
\*--------------------------------------------------------------------*/
int t_magic_university::get_version() const
{
	return 2;
}

// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
float t_magic_university::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	if (!player_can_afford( &army, k_skill_upgrade_cost))
		return 0.0f;

	// This doesn't take into account the number of skills the player can afford (beyond the first).
	if (m_visited[get_player_number(army)])
		return sum_available_skill_values( army );
	else
	{
		float cost = k_skill_upgrade_cost * army.get_owner()->get_material_value( k_gold );
		return ai_summed_skill_value(cost, k_skill_healing_magic, k_skill_recruitment, &army );
	}
}

float t_magic_university::sum_available_skill_values(t_creature_array const& array) const
{
	float value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero  = array[i].get_const_hero();
		if (!hero) continue;
		for (int i = 0; i < 4; i++) 
		{
			t_skill_type type = m_skills[i];
			if ( (hero->get_skill(type) == k_mastery_none) &&
				hero->can_learn(t_skill(type, k_mastery_basic)) )
			{
				float skill_value = ai_value_of_skill_increase_minus_cost(hero, t_skill(type, k_mastery_basic), k_skill_upgrade_cost, &array);
				if (skill_value > 0.0f) 
					value += skill_value;
			}
		}
	}
	return value;
}

t_skill_type t_magic_university::get_best_compatible_skill( t_hero const* hero, t_creature_array const* array ) const
{
	t_skill_type best_type = k_skill_none;
	int best_index = -1;

	if (!player_can_afford( array, k_skill_upgrade_cost))
		return k_skill_none;

	float best_value = 0.0f;
	for (int i = 0; i < 4; i++) 
	{
		t_skill_type type = m_skills[i];
		if ( (hero->get_skill(type) == k_mastery_none) &&
			hero->can_learn(t_skill(type, k_mastery_basic)) )
		{
			float skill_value = ai_value_of_skill_increase_minus_cost(hero, t_skill(type, k_mastery_basic), k_skill_upgrade_cost, array);
			if (skill_value > best_value) 
			{
				best_value = skill_value;
				best_type = type;
			}
		}
	}
	return best_type;
}


/*-----------------------------------------------------------------------------*\
**
** War College / War University
**
** - Selected hero can buy secondary war skills.
**
\*-----------------------------------------------------------------------------*/
t_war_college::t_war_college( t_stationary_adventure_object const& source )
        : t_magic_university (source)
{
}

/*--------------------------------------------------------------------*\
** Default available skills for old map files
\*--------------------------------------------------------------------*/
t_skill_set const & t_war_college::get_default_available_skill_set()
{
	return ::get_skill_set(false, true, false, true);
}

float t_war_college::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	if (!player_can_afford( &army, k_skill_upgrade_cost))
		return 0.0f;

	// This doesn't take into account the number of skills the player can afford (beyond the first).
	if (m_visited[get_player_number(army)])
		return sum_available_skill_values( army );
	else 
	{
		float cost = k_skill_upgrade_cost * army.get_owner()->get_material_value( k_gold );
		return ai_summed_skill_value(cost, k_skill_offense, k_skill_diplomacy, &army );
	}
}


